/********************************** (C) COPYRIGHT *******************************
 * File Name          : main.c
 * Author             : WCH
 * Version            : V1.0.0
 * Date               : 2022/05/31
 * Description        : Main program body.
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * SPDX-License-Identifier: Apache-2.0
 *******************************************************************************/
/*
 *@Note
 TCP Client例程，演示TCP client连接服务器后接收数据再回传
 */

#include "string.h"
#include "debug.h"
#include "wchnet.h"
#include "eth_driver.h"

#include "cJSON.h"



#define KEEPLIVE_ENABLE         0                       //Enable keeplive function

u8 MACAddr[6];                                          //MAC address
u8 IPAddr[4] =   { 0, 0, 0, 0 };                        //IP address
u8 GWIPAddr[4] = { 0, 0, 0, 0 };                        //Gateway IP address
u8 IPMask[4] =   { 0, 0, 0, 0 };                        //subnet mask
//u8 DESIP[4] = { 192,168,1,177 };                      //destination IP address
uint8_t DESIP[4] = {0,0,0,0};
uint8_t dns[4] = {0,0,0,0};

#define API_DOMAIN  "restapi.amap.com"
#define API_CITY    "340100"
#define API_KEY     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

__attribute__((__aligned__(4))) u8  RemoteIp[4]={0};
u16 desport = 80;                                         //destination port
u16 srcport = 10255;                                         //source port
u16 DnsPort = 53;
u8 SocketId;
u8 socket[WCHNET_MAX_SOCKET_NUM];                           //Save the currently connected socket
u8 SocketRecvBuf[WCHNET_MAX_SOCKET_NUM][RECE_BUF_LEN];      //socket receive buffer
u8 MyBuf[RECE_BUF_LEN];

//static uint8_t tcp_state;


enum{
    WORK_WAIT_PHY_READY,
    WOKK_DHCP_START,
    WORK_WAIT_DHCP_DONE,
    WORK_DNS_START,
    WORK_WAIT_DNS_DONE,
    WORK_TCP_START,
    WORK_WAIT_TCP_DONE,
    WORK_HTTP_GET,
    WORK_WAIT_HTTP_GET,
    WORK_HTTP_DONE
};
static uint8_t work_state = WORK_WAIT_PHY_READY;

void work_state_set(uint8_t sta) {
    work_state = sta;
}

const char header[] = \
            "GET /v3/weather/weatherInfo?city=%s&key=%s&extensions=base HTTP/1.1\r\n" \
            "Host: %s\r\n"\
            "User-Agent: curl/7.85.0\r\n"\
            "Accept: */*\r\n"\
            "\r\n";


/*********************************************************************
 * @fn      mStopIfError
 *
 * @brief   check if error.
 *
 * @param   iError - error constants.
 *
 * @return  none
 */
void mStopIfError(u8 iError)
{
    if (iError == WCHNET_ERR_SUCCESS)
        return;
    printf("Error: %02X\r\n", (u16) iError);
}

/*********************************************************************
 * @fn      TIM2_Init
 *
 * @brief   Initializes TIM2.
 *
 * @return  none
 */
void TIM2_Init(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = { 0 };

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_TimeBaseStructure.TIM_Period = SystemCoreClock / 1000000 - 1;
    TIM_TimeBaseStructure.TIM_Prescaler = WCHNETTIMERPERIOD * 1000 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

    TIM_Cmd(TIM2, ENABLE);
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    NVIC_EnableIRQ(TIM2_IRQn);
}

/*********************************************************************
 * @fn      WCHNET_CreateTcpSocket
 *
 * @brief   Create TCP Socket
 *
 * @return  none
 */
void WCHNET_CreateTcpSocket(void)
{
    u8 i;
    SOCK_INF TmpSocketInf;

    memset((void *) &TmpSocketInf, 0, sizeof(SOCK_INF));
    memcpy((void *) TmpSocketInf.IPAddr, DESIP, 4);
    TmpSocketInf.DesPort = desport;
    TmpSocketInf.SourPort = srcport++;
    TmpSocketInf.ProtoType = PROTO_TYPE_TCP;
    TmpSocketInf.RecvBufLen = RECE_BUF_LEN;
    i = WCHNET_SocketCreat(&SocketId, &TmpSocketInf);
    printf("WCHNET_SocketCreat %d\r\n", SocketId);
    mStopIfError(i);
    i = WCHNET_SocketConnect(SocketId);                        //make a TCP connection
    mStopIfError(i);
}

/*********************************************************************
 * @fn      WCHNET_DataLoopback
 *
 * @brief   Data loopback function.
 *
 * @param   id - socket id.
 *
 * @return  none
 */
void WCHNET_DataLoopback(u8 id)
{
#if 1

    u32 len;
    u32 endAddr = SocketInf[id].RecvStartPoint + SocketInf[id].RecvBufLen;       //Receive buffer end address

    if ((SocketInf[id].RecvReadPoint + SocketInf[id].RecvRemLen) > endAddr) {    //Calculate the length of the received data
        len = endAddr - SocketInf[id].RecvReadPoint;
    }
    else {
        len = SocketInf[id].RecvRemLen;
    }
    PRINT("rx,id:%u,len:%04u\n",id,len);
    //printf("%s\n",(u8 *) SocketInf[id].RecvReadPoint);
    char *json_start = strstr((char *)SocketInf[id].RecvReadPoint,"{\"");
    if(json_start == NULL) {
        WCHNET_SocketRecv(id, NULL, &len);
        return ;
    }
    //PRINT("json start:%s\n",json_start);
    cJSON *root;
    char *out;
    root = cJSON_Parse(json_start); //获取整个大的句柄
    if(!root) {
        PRINT("Error before: [%s]\n",cJSON_GetErrorPtr());
    }

    //PRINT("format   :%s\n",out);
    //PRINT("unformat :%s\n",cJSON_PrintUnformatted(root));

    cJSON *arrayItem = cJSON_GetObjectItem(root,"status");
    out = cJSON_Print(arrayItem);
    //PRINT("%s\n",out);
    cJSON_free(out);

    arrayItem = cJSON_GetObjectItem(root,"info");
    out = cJSON_Print(arrayItem);
    //PRINT("%s\n",out);
    cJSON_free(out);

    arrayItem = cJSON_GetObjectItem(root,"infocode");
    out = cJSON_Print(arrayItem);
    PRINT("%s\n",out);
    cJSON_free(out);

    arrayItem = cJSON_GetObjectItem(root,"lives");
    out = cJSON_Print(arrayItem);
    PRINT("%s\n",out);
    cJSON_free(out);

    arrayItem = cJSON_GetArrayItem(arrayItem,0);
    out = cJSON_Print(arrayItem);
    PRINT("%s\n",out);
    cJSON_free(out);

    cJSON *item;
    item = cJSON_GetObjectItem(arrayItem,"province");
    out = cJSON_Print(item);
    PRINT("province:%s\n",out);
    cJSON_free(out);
    item = cJSON_GetObjectItem(arrayItem,"city");
    out = cJSON_Print(item);
    PRINT("city:%s\n",out);
    cJSON_free(out);
    item = cJSON_GetObjectItem(arrayItem,"adcode");
    out = cJSON_Print(item);
    PRINT("adcode:%s\n",out);
    cJSON_free(out);
    item = cJSON_GetObjectItem(arrayItem,"weather");
    out = cJSON_Print(item);
    PRINT("weather:%s\n",out);
    cJSON_free(out);
    item = cJSON_GetObjectItem(arrayItem,"temperature");
    out = cJSON_Print(item);
    PRINT("temperature:%s\n",out);
    cJSON_free(out);
    item = cJSON_GetObjectItem(arrayItem,"winddirection");
    out = cJSON_Print(item);
    PRINT("winddirection:%s\n",out);
    cJSON_free(out);
    item = cJSON_GetObjectItem(arrayItem,"windpower");
    out = cJSON_Print(item);
    PRINT("windpower:%s\n",out);
    cJSON_free(out);
    item = cJSON_GetObjectItem(arrayItem,"humidity");
    out = cJSON_Print(item);
    PRINT("humidity:%s\n",out);
    cJSON_free(out);

    item = cJSON_GetObjectItem(arrayItem,"reporttime");
    out = cJSON_Print(item);
    PRINT("reporttime:%s\n",out);
    cJSON_free(out);


    cJSON_Delete(root);
    WCHNET_SocketRecv(id, NULL, &len);

#if 0
    i = WCHNET_SocketSend(id, (u8 *) SocketInf[id].RecvReadPoint, &len);         //send data
    if (i == WCHNET_ERR_SUCCESS) {
        WCHNET_SocketRecv(id, NULL, &len);                                       //Clear sent data
    }
#endif
#else
    u32 len, totallen;
    u8 *p = MyBuf;

    len = WCHNET_SocketRecvLen(id, NULL);                                //query length
    WCHNET_SocketRecv(id, MyBuf, &len);                                  //Read the data of the receive buffer into MyBuf
    totallen = len;
    while(1){
        len = totallen;
        WCHNET_SocketSend(id, p, &len);                                  //Send the data
        totallen -= len;                                                 //Subtract the sent length from the total length
        p += len;                                                        //offset buffer pointer
        if(totallen) continue;                                           //If the data is not sent, continue to send
        break;                                                           //After sending, exit
    }
#endif
}

/*********************************************************************
 * @fn      WCHNET_HandleSockInt
 *
 * @brief   Socket Interrupt Handle
 *
 * @param   socketid - socket id.
 *          intstat - interrupt status
 *
 * @return  none
 */
void WCHNET_HandleSockInt(u8 socketid, u8 intstat)
{
    u8 i;

    if (intstat & SINT_STAT_RECV)                              //receive data
    {
        WCHNET_DataLoopback(socketid);                         //Data loopback
    }
    if (intstat & SINT_STAT_CONNECT)                           //connect successfully
    {
#if KEEPLIVE_ENABLE
        WCHNET_SocketSetKeepLive(socketid, ENABLE);
#endif
        WCHNET_ModifyRecvBuf(socketid, (u32) SocketRecvBuf[socketid], RECE_BUF_LEN);
        for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) {
            if (socket[i] == 0xff) {                           //save connected socket id
                socket[i] = socketid;
                break;
            }
        }
        printf("TCP Connect Success\r\n");
        printf("socket id: %d\r\n", socket[i]);

        char message[1024];

        sprintf(message,header,API_CITY,API_KEY ,API_DOMAIN);
        uint32_t send_len,len;
        send_len = strlen(message);
        len = send_len;
        i = WCHNET_SocketSend(SocketId,message,&len);
        PRINT("send:%02x,len:%u\n",i,len);
        work_state_set(WORK_WAIT_HTTP_GET);
#if 0
        send_len = strlen(post_data);
        len = send_len;
        i = WCHNET_SocketSend(SocketId,post_data,&len);
        PRINT("send:%02x,len:%u\n",i,len);
#endif



    }
    if (intstat & SINT_STAT_DISCONNECT)                         //disconnect
    {
        for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) {           //delete disconnected socket id
            if (socket[i] == socketid) {
                socket[i] = 0xff;
                break;
            }
        }
        printf("TCP Disconnect\r\n");
        work_state_set(WORK_HTTP_DONE);
    }
    if (intstat & SINT_STAT_TIM_OUT)                             //timeout disconnect
    {
        for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) {            //delete disconnected socket id
            if (socket[i] == socketid) {
                socket[i] = 0xff;
                break;
            }
        }
        printf("TCP Timeout\r\n");
        work_state_set(WORK_HTTP_DONE);
        //WCHNET_CreateTcpSocket();
    }
}

/*********************************************************************
 * @fn      WCHNET_HandleGlobalInt
 *
 * @brief   Global Interrupt Handle
 *
 * @return  none
 */
void WCHNET_HandleGlobalInt(void)
{
    u8 intstat;
    u16 i;
    u8 socketint;

    intstat = WCHNET_GetGlobalInt();                              //get global interrupt flag
    if (intstat & GINT_STAT_SOCKET) {                             //socket related interrupt
        for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++) {
            socketint = WCHNET_GetSocketInt(i);
            if (socketint)
                WCHNET_HandleSockInt(i, socketint);
        }
    }
    if (intstat & GINT_STAT_UNREACH)                              //Unreachable interrupt
    {
        printf("GINT_STAT_UNREACH\r\n");
    }
    if (intstat & GINT_STAT_IP_CONFLI)                            //IP conflict
    {
        printf("GINT_STAT_IP_CONFLI\r\n");
    }
    if (intstat & GINT_STAT_PHY_CHANGE)                           //PHY status change
    {
        i = WCHNET_GetPHYStatus();
        PRINT("phy change:%02X\n",i);
        if (i & PHY_Linked_Status)
            printf("PHY Link Success\r\n");
    }

}

void WCHNET_DNSCallBack(const char *name, u8 *ipaddr, void *callback_arg)
{
    if(ipaddr == NULL)
    {
        printf("DNS Fail\r\n");
        return;
    }
    printf("Host Name = %s\r\n", name);
    printf("IP= %d.%d.%d.%d\r\n", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]);
    if(callback_arg != NULL)
    {
        printf("callback_arg = %02x\r\n", (*(u8 *)callback_arg));
    }
    WCHNET_DNSStop();                                                          //stop DNS,and release socket
    memcpy(DESIP,ipaddr,4);
    //WCHNET_CreateTcpSocket();

}



/*********************************************************************
 * @fn      WCHNET_DHCPCallBack
 *
 * @brief   DHCPCallBack
 *
 * @param   status - status returned by DHCP
 *          arg - Data returned by DHCP
 *
 * @return  DHCP status
 */
u8 WCHNET_DHCPCallBack(u8 status, void *arg)
{
    u8 *p;
    u8 tmp[4] = {0, 0, 0, 0};
    if(!status)
    {
        p = arg;
        printf("DHCP Success\r\n");
        /*If the obtained IP is the same as the last IP, exit this function.*/
        if(!memcmp(IPAddr, p ,sizeof(IPAddr)))
            return READY;
        /*Determine whether it is the first successful IP acquisition*/
        if(memcmp(IPAddr, tmp ,sizeof(IPAddr))){
            /*The obtained IP is different from the last value,
             * then disconnect the last connection.*/
            WCHNET_SocketClose(SocketId, TCP_CLOSE_NORMAL);
        }
        memcpy(IPAddr, p, 4);
        memcpy(GWIPAddr, &p[4], 4);
        memcpy(IPMask, &p[8], 4);
        printf("IPAddr = %d.%d.%d.%d \r\n", (u16)IPAddr[0], (u16)IPAddr[1],
               (u16)IPAddr[2], (u16)IPAddr[3]);
        printf("GWIPAddr = %d.%d.%d.%d \r\n", (u16)GWIPAddr[0], (u16)GWIPAddr[1],
               (u16)GWIPAddr[2], (u16)GWIPAddr[3]);
        printf("IPAddr = %d.%d.%d.%d \r\n", (u16)IPMask[0], (u16)IPMask[1],
               (u16)IPMask[2], (u16)IPMask[3]);
        printf("DNS1: %d.%d.%d.%d \r\n", p[12], p[13], p[14], p[15]);
        printf("DNS2: %d.%d.%d.%d \r\n", p[16], p[17], p[18], p[19]);
        memcpy(dns,&p[12],4);
#if 0
        //WCHNET_CreateTcpSocket();                                                   //Create a TCP connection
        PRINT("start dns name lookup:%s\n",API_DOMAIN);
        WCHNET_InitDNS(&p[12], 53);                                         //Set DNS server IP address, and DNS server port is 53
        i = WCHNET_HostNameGetIp(API_DOMAIN, RemoteIp, WCHNET_DNSCallBack, NULL);  //Start DNS
        PRINT("WCHNET_HostNameGetIp:%02x\n",i);
#endif
        return READY;
    }
    else
    {
        printf("DHCP Fail %02x \r\n", status);
        /*Determine whether it is the first successful IP acquisition*/
        if(memcmp(IPAddr, tmp ,sizeof(IPAddr))){
            /*The obtained IP is different from the last value*/
            WCHNET_SocketClose(SocketId, TCP_CLOSE_NORMAL);
        }
        return NoREADY;
    }
}

extern uint32_t volatile LocalTime;
void work_process(void) {

    uint16_t ret;
    static uint32_t last_api_time;
    switch(work_state) {
        case WORK_WAIT_PHY_READY:
            ret = WCHNET_GetPHYStatus();

            if (ret & PHY_Linked_Status) {
                printf("PHY Link Success\r\n");

                work_state = WOKK_DHCP_START;
            }
            break;
        case WOKK_DHCP_START:
            memset(GWIPAddr,0,4);
            memset(IPAddr,0,4);
            memset(dns,0,4);
            memset(IPMask,0,4);
            ret = WCHNET_DHCPStart(WCHNET_DHCPCallBack);
            PRINT("WCHNET_DHCPStart:%02x\n",ret);
            work_state = WORK_WAIT_DHCP_DONE;
            break;
        case WORK_WAIT_DHCP_DONE:
            if(GWIPAddr[0] != 0) {
                PRINT("gw   :%d.%d.%d.%d\n",GWIPAddr[0],GWIPAddr[1],GWIPAddr[2],GWIPAddr[3]);
                PRINT("ip   :%d.%d.%d.%d\n",IPAddr[0],IPAddr[1],IPAddr[2],IPAddr[3]);
                PRINT("mask :%d.%d.%d.%d\n",IPMask[0],IPMask[1],IPMask[2],IPMask[3]);
                PRINT("dns  :%d.%d.%d.%d\n",dns[0],dns[1],dns[2],dns[3]);
                work_state = WORK_DNS_START;
            }
            break;
        case WORK_DNS_START:
            if(dns[0] != 0) {
                memset(DESIP,0,4);
                #if 1                                             //Create a TCP connection
                PRINT("start dns name lookup:%s\n",API_DOMAIN);
                WCHNET_InitDNS(dns, 53);                                         //Set DNS server IP address, and DNS server port is 53
                ret = WCHNET_HostNameGetIp(API_DOMAIN, RemoteIp, WCHNET_DNSCallBack, NULL);  //Start DNS
                PRINT("WCHNET_HostNameGetIp:%02x\n",ret);
                #endif
                work_state = WORK_WAIT_DNS_DONE;
            }
            break;
        case WORK_WAIT_DNS_DONE:
            if(DESIP[0] != 0) {
                PRINT("des ip :%d.%d.%d.%d\n",DESIP[0],DESIP[1],DESIP[2],DESIP[3]);

                work_state = WORK_TCP_START;
            }
            break;
        case WORK_TCP_START:
            WCHNET_CreateTcpSocket();
            work_state = WORK_WAIT_TCP_DONE;
            last_api_time = LocalTime;
            PRINT("last_api_time:%u\n",last_api_time);
            break;
        case WORK_WAIT_TCP_DONE:
            break;

        case WORK_HTTP_GET:
            break;
        case WORK_WAIT_HTTP_GET:
            break;
        case WORK_HTTP_DONE:
            if(((LocalTime - last_api_time)&0xffffffff) >10000) {
                work_state = WORK_TCP_START;
            }
            //PRINT("last_api_time HTTP:%u\n",LocalTime);
            //Delay_Us(1000);
            break;
        default:
            break;
    }
}



/*********************************************************************
 * @fn      main
 *
 * @brief   Main program
 *
 * @return  none
 */
int main(void)
{
    u8 i;

    Delay_Init();

    USART_Printf_Init(115200);                                   //USART initialize
    PRINT("http get test\r\n");
    PRINT("SystemClk:%d\r\n", SystemCoreClock);
    PRINT("net version:%x\n", WCHNET_GetVer());
    if ( WCHNET_LIB_VER != WCHNET_GetVer()) {
        PRINT("version error.\n");
    }
    WCHNET_GetMacAddr(MACAddr);                                  //get the chip MAC address
    PRINT("mac addr:");
    for ( i = 0; i < 6; i++)
        PRINT("%x ", MACAddr[i]);
    PRINT("\n");

    TIM2_Init();
    WCHNET_DHCPSetHostname("WCHNET");
    PRINT("WCHNET_DHCPSetHostname：%02x\n",i);
    i = ETH_LibInit(IPAddr, GWIPAddr, IPMask, MACAddr);          //Ethernet library initialize
    mStopIfError(i);
    if (i == WCHNET_ERR_SUCCESS)
        printf("WCHNET_LibInit Success\r\n");
#if KEEPLIVE_ENABLE                                              //Configure keeplive parameters
    {
        struct _KEEP_CFG cfg;

        cfg.KLIdle = 20000;
        cfg.KLIntvl = 15000;
        cfg.KLCount = 9;
        WCHNET_ConfigKeepLive(&cfg);
    }
#endif
    memset(socket, 0xff, WCHNET_MAX_SOCKET_NUM);
    //for (i = 0; i < WCHNET_MAX_SOCKET_NUM; i++)
    //for (i = 0; i < 1; i++);

    //i = WCHNET_DHCPStart(WCHNET_DHCPCallBack);
    //PRINT("WCHNET_DHCPStart：%02x\n",i);
    while(1)
    {
        /*Ethernet library main task function,
         * which needs to be called cyclically*/
        WCHNET_MainTask();
        /*Query the Ethernet global interrupt,
         * if there is an interrupt, call the global interrupt handler*/
        if(WCHNET_QueryGlobalInt())
        {
            WCHNET_HandleGlobalInt();
        }
        work_process();

    }
}
